#include "commons.h"
#include <stdio.h>
#include <stdlib.h>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void reset_m6502() {
    //emu->m6502->pc.w = 0xc000; // FIXME: nestest.nes
    emu->m6502->pc.l = read_memory( 0xfffc );
    emu->m6502->pc.h = read_memory( 0xfffd );
    emu->m6502->a = 0;
    emu->m6502->x = 0;
    emu->m6502->y = 0;
    emu->m6502->sp = 0xfd;
    emu->m6502->status = 0x24;
    emu->m6502->ticks = 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void nmi_interrupt() {
    //printf("Entering NMI\n"); getch();
    stack_push( emu->m6502->pc.h );
    stack_push( emu->m6502->pc.l );
    stack_push( (BYTE)(emu->m6502->status | 0x20) );
    emu->m6502->status |= FLAG_I;
    emu->m6502->pc.h = read_memory( 0xfffb );
    emu->m6502->pc.l = read_memory( 0xfffa );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BYTE fetch_opcode() {
    return read_memory( emu->m6502->pc.w++ );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void execute_opcode( BYTE opcode ) {
    //log_trace( opcode );
    emu->m6502->ticks = 0;
    
    switch( opcode ) {
        case 0x01: opcode_ora( IND_X ); break;
        case 0x05: opcode_ora( ZP ); break;
        case 0x06: opcode_asl( ZP ); break;
        case 0x08: opcode_php(); break;
        case 0x09: opcode_ora( IMM ); break;
        case 0x0a: opcode_asl( ACC ); break;
        case 0x0d: opcode_ora( ABS ); break;
        case 0x0e: opcode_asl( ABS ); break;
        case 0x10: opcode_bpl(); break;
        case 0x11: opcode_ora( IND_Y ); break;
        case 0x15: opcode_ora( ZP_X ); break;
        case 0x16: opcode_asl( ZP_X ); break;
        case 0x18: opcode_clc(); break;
        case 0x19: opcode_ora( ABS_Y ); break;
        case 0x1d: opcode_ora( ABS_X ); break;
        case 0x1e: opcode_asl( ABS_X_NC ); break;
        case 0x20: opcode_jsr(); break;
        case 0x21: opcode_and( IND_X ); break;
        case 0x24: opcode_bit( ZP ); break;
        case 0x25: opcode_and( ZP ); break;
        case 0x26: opcode_rol( ZP ); break;
        case 0x28: opcode_plp(); break;
        case 0x29: opcode_and( IMM ); break;
        case 0x2a: opcode_rol( ACC ); break;
        case 0x2c: opcode_bit( ABS ); break;
        case 0x2d: opcode_and( ABS ); break;
        case 0x2e: opcode_rol( ABS ); break;
        case 0x30: opcode_bmi(); break;
        case 0x31: opcode_and( IND_Y ); break;
        case 0x35: opcode_and( ZP_X ); break;
        case 0x36: opcode_rol( ZP_X ); break;
        case 0x38: opcode_sec(); break;
        case 0x39: opcode_and( ABS_Y ); break;
        case 0x3d: opcode_and( ABS_X ); break;
        case 0x3e: opcode_rol( ABS_X_NC ); break;
        case 0x40: opcode_rti(); break;
        case 0x41: opcode_eor( IND_X ); break;
        case 0x45: opcode_eor( ZP ); break;
        case 0x46: opcode_lsr( ZP ); break;
        case 0x48: opcode_pha(); break;
        case 0x49: opcode_eor( IMM ); break;
        case 0x4a: opcode_lsr( ACC ); break;
        case 0x4c: opcode_jmp( ABS ); break;
        case 0x4d: opcode_eor( ABS ); break;
        case 0x4e: opcode_lsr( ABS ); break;
        case 0x50: opcode_bvc(); break;
        case 0x51: opcode_eor( IND_Y ); break;
        case 0x55: opcode_eor( ZP_X ); break;
        case 0x56: opcode_lsr( ZP_X ); break;
        case 0x59: opcode_eor( ABS_Y ); break;
        case 0x5d: opcode_eor( ABS_X ); break;
        case 0x5e: opcode_lsr( ABS_X ); break;
        case 0x60: opcode_rts(); break;
        case 0x61: opcode_adc( IND_X ); break;
        case 0x65: opcode_adc( ZP ); break;
        case 0x66: opcode_ror( ZP ); break;
        case 0x68: opcode_pla(); break;
        case 0x69: opcode_adc( IMM ); break;
        case 0x6a: opcode_ror( ACC ); break;
        case 0x6c: opcode_jmp( IND ); break;
        case 0x6d: opcode_adc( ABS ); break;
        case 0x6e: opcode_ror( ABS ); break;
        case 0x70: opcode_bvs(); break;
        case 0x71: opcode_adc( IND_Y ); break;
        case 0x75: opcode_adc( ZP_X ); break;
        case 0x76: opcode_ror( ZP_X ); break;
        case 0x78: opcode_sei(); break;
        case 0x79: opcode_adc( ABS_Y ); break;
        case 0x7d: opcode_adc( ABS_X ); break;
        case 0x7e: opcode_ror( ABS_X_NC ); break;
        case 0x81: opcode_sta( IND_X ); break;
        case 0x84: opcode_sty( ZP ); break;
        case 0x85: opcode_sta( ZP ); break;
        case 0x86: opcode_stx( ZP ); break;
        case 0x88: opcode_dey(); break;
        case 0x8a: opcode_txa(); break;
        case 0x8c: opcode_sty( ABS ); break;
        case 0x8d: opcode_sta( ABS ); break;
        case 0x8e: opcode_stx( ABS ); break;
        case 0x90: opcode_bcc(); break;
        case 0x91: opcode_sta( IND_Y_NC ); break;
        case 0x94: opcode_sty( ZP_X ); break;
        case 0x95: opcode_sta( ZP_X ); break;
        case 0x96: opcode_stx( ZP_Y ); break;
        case 0x98: opcode_tya(); break;
        case 0x99: opcode_sta( ABS_Y_NC ); break;
        case 0x9a: opcode_txs(); break;
        case 0x9d: opcode_sta( ABS_X_NC ); break;
        case 0xa0: opcode_ldy( IMM ); break;
        case 0xa1: opcode_lda( IND_X ); break;
        case 0xa2: opcode_ldx( IMM ); break;
        case 0xa4: opcode_ldy( ZP ); break;
        case 0xa5: opcode_lda( ZP ); break;
        case 0xa6: opcode_ldx( ZP ); break;
        case 0xa8: opcode_tay(); break;
        case 0xa9: opcode_lda( IMM ); break;
        case 0xaa: opcode_tax(); break;
        case 0xac: opcode_ldy( ABS ); break;
        case 0xad: opcode_lda( ABS ); break;
        case 0xae: opcode_ldx( ABS ); break;
        case 0xb0: opcode_bcs(); break;
        case 0xb1: opcode_lda( IND_Y ); break;
        case 0xb4: opcode_ldy( ZP_X ); break;
        case 0xb5: opcode_lda( ZP_X ); break;
        case 0xb6: opcode_ldx( ZP_Y ); break;
        case 0xb8: opcode_clv(); break;
        case 0xb9: opcode_lda( ABS_Y ); break;
        case 0xba: opcode_tsx(); break;
        case 0xbc: opcode_ldy( ABS_X ); break;
        case 0xbd: opcode_lda( ABS_X ); break;
        case 0xbe: opcode_ldx( ABS_Y ); break;
        case 0xc0: opcode_cpy( IMM ); break;
        case 0xc1: opcode_cmp( IND_X ); break;        
        case 0xc4: opcode_cpy( ZP ); break;
        case 0xc5: opcode_cmp( ZP ); break;
        case 0xc6: opcode_dec( ZP ); break;
        case 0xc8: opcode_iny(); break;
        case 0xc9: opcode_cmp( IMM ); break;
        case 0xca: opcode_dex(); break;
        case 0xcc: opcode_cpy( ABS ); break;
        case 0xcd: opcode_cmp( ABS ); break;
        case 0xce: opcode_dec( ABS ); break;
        case 0xd0: opcode_bne(); break;
        case 0xd1: opcode_cmp( IND_Y ); break;
        case 0xd5: opcode_cmp( ZP_X ); break;
        case 0xd6: opcode_dec( ZP_X ); break;
        case 0xd8: opcode_cld(); break;
        case 0xd9: opcode_cmp( ABS_Y ); break;
        case 0xdd: opcode_cmp( ABS_X ); break;
        case 0xde: opcode_dec( ABS_X_NC ); break;
        case 0xe0: opcode_cpx( IMM ); break;
        case 0xe1: opcode_sbc( IND_X ); break;
        case 0xe4: opcode_cpx( ZP ); break;
        case 0xe5: opcode_sbc( ZP ); break;
        case 0xe6: opcode_inc( ZP ); break;
        case 0xe8: opcode_inx(); break;
        case 0xe9: opcode_sbc( IMM ); break;
        case 0xea: opcode_nop(); break;
        case 0xec: opcode_cpx( ABS ); break;
        case 0xed: opcode_sbc( ABS ); break;
        case 0xee: opcode_inc( ABS ); break;
        case 0xf0: opcode_beq(); break;
        case 0xf1: opcode_sbc( IND_Y ); break;
        case 0xf5: opcode_sbc( ZP_X ); break;
        case 0xf6: opcode_inc( ZP_X ); break;
        case 0xf8: opcode_sed(); break;
        case 0xf9: opcode_sbc( ABS_Y ); break;
        case 0xfd: opcode_sbc( ABS_X ); break;
        case 0xfe: opcode_inc( ABS_X_NC ); break;
        default: printf( " opcode desconocido" ); exit( 0 );
    }
    
    emu->m6502->ticks += OPCODE_CYCLES[ opcode ] * BASE_TIME;
    //if( emu->m6502->ticks >= 341 ) emu->m6502->ticks -= 341;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void stack_push( BYTE value ) {
    write_memory( (WORD)(emu->m6502->sp-- | 0x0100), value );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BYTE stack_pop() {
    return read_memory( (WORD)(++emu->m6502->sp | 0x0100) );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int test_flag( int flag ) {
    return ( emu->m6502->status & flag ) == flag ? 1 : 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
WORD solve_address( int mode ) {
    switch( mode ) {
        case ABS: return address_abs();
        case ABS_X: return address_abs_x();
        case ABS_X_NC: return address_abs_x_nc();
        case ABS_Y: return address_abs_y();
        case ABS_Y_NC: return address_abs_y_nc();
        case IMM: return address_imm();
        case IND: return address_ind();
        case IND_X: return address_ind_x();
        case IND_Y: return address_ind_y();
        case IND_Y_NC: return address_ind_y_nc();
        case REL: return address_rel();
        case ZP: return address_zp();
        case ZP_X: return address_zp_x();
        case ZP_Y: return address_zp_y();
        default: printf( "\nError: <%i> no es un modo de direccionamiento valido", mode ); exit( 0 );
    }
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
WORD address_abs() {
    PAIR_REGISTER temp;
    
    temp.l = read_memory( emu->m6502->pc.w++ );
    temp.h = read_memory( emu->m6502->pc.w++ );
    
    return temp.w;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
WORD address_abs_x() {
    PAIR_REGISTER temp;
    
    temp.l = read_memory( emu->m6502->pc.w++ );
    temp.h = read_memory( emu->m6502->pc.w++ );
    
    BYTE before = temp.h;
    temp.w += emu->m6502->x;
    if( before != temp.h ) emu->m6502->ticks += 1 * BASE_TIME;
    
    return temp.w;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
WORD address_abs_x_nc() {
    PAIR_REGISTER temp;
    
    temp.l = read_memory( emu->m6502->pc.w++ );
    temp.h = read_memory( emu->m6502->pc.w++ );
    temp.w += emu->m6502->x;
    
    return temp.w;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
WORD address_abs_y() {
    PAIR_REGISTER temp;
    
    temp.l = read_memory( emu->m6502->pc.w++ );
    temp.h = read_memory( emu->m6502->pc.w++ );
    
    BYTE before = temp.h;
    temp.w += emu->m6502->y;
    if( before != temp.h ) emu->m6502->ticks += 1 * BASE_TIME;
    
    return temp.w;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
WORD address_abs_y_nc() {
    PAIR_REGISTER temp;
    
    temp.l = read_memory( emu->m6502->pc.w++ );
    temp.h = read_memory( emu->m6502->pc.w++ );
    temp.w += emu->m6502->y;
    
    return temp.w;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
WORD address_imm() {
    return emu->m6502->pc.w++;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
WORD address_ind_x() {
    PAIR_REGISTER temp;

    temp.l = read_memory( (BYTE)(read_memory(emu->m6502->pc.w) + emu->m6502->x) );
    temp.h = read_memory( (BYTE)(read_memory(emu->m6502->pc.w++) + emu->m6502->x + 1) );

    return temp.w;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
WORD address_ind() {
    PAIR_REGISTER temp_1, temp_2;
    
    temp_1.l = read_memory( emu->m6502->pc.w++ );
    temp_1.h = read_memory( emu->m6502->pc.w );
    
    temp_2.l = read_memory( temp_1.w );
    temp_1.l++;
    temp_2.h = read_memory( temp_1.w );
    
    return temp_2.w;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
WORD address_ind_y() {
    PAIR_REGISTER temp;
    
    temp.l = read_memory( read_memory(emu->m6502->pc.w) );
    temp.h = read_memory( (BYTE)(read_memory(emu->m6502->pc.w++) + 1) );
    
    BYTE before = temp.h;
    temp.w += emu->m6502->y;
    if( before != temp.h ) emu->m6502->ticks += 1 * BASE_TIME;
    
    return temp.w;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
WORD address_ind_y_nc() {
    PAIR_REGISTER temp;
    
    temp.l = read_memory( read_memory(emu->m6502->pc.w) );
    temp.h = read_memory( (BYTE)(read_memory(emu->m6502->pc.w++) + 1) );
    temp.w += emu->m6502->y;
    
    return temp.w;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
WORD address_rel() {
    BYTE before = emu->m6502->pc.h;
    SIGNED_WORD displacement = read_memory( (WORD)(emu->m6502->pc.w - 1) );
    if( test_bit((BYTE)displacement, 7) ) displacement -= 0x0100;
    emu->m6502->pc.w = ( WORD )( emu->m6502->pc.w + displacement );
    emu->m6502->ticks += ( before != emu->m6502->pc.h ? 2 : 1 ) * BASE_TIME;
    
    return emu->m6502->pc.w;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
WORD address_zp() {
    PAIR_REGISTER temp;
    
    temp.l = read_memory( emu->m6502->pc.w++ );
    temp.h = 0;
    
    return temp.w;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
WORD address_zp_x() {
    PAIR_REGISTER temp;
    
    temp.l = read_memory( emu->m6502->pc.w++ ) + emu->m6502->x;
    temp.h = 0;
    
    return temp.w;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
WORD address_zp_y() {
    PAIR_REGISTER temp;
    
    temp.l = read_memory( emu->m6502->pc.w++ ) + emu->m6502->y;
    temp.h = 0;
    
    return temp.w;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void solve_flags_nz( BYTE value ) {
    emu->m6502->status &= ~( FLAG_N | FLAG_Z );
    if( !value ) emu->m6502->status |= FLAG_Z;
    if( test_bit(value, 7) ) emu->m6502->status |= FLAG_N;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_adc( int mode ) {
    BYTE value = read_memory( solve_address(mode) );
    WORD result = ( WORD )( emu->m6502->a + value + test_flag(FLAG_C) );
    emu->m6502->status &= ~( FLAG_C | FLAG_V );
    if( result > 0x00ff ) emu->m6502->status |= FLAG_C;
    if( !test_bit(emu->m6502->a ^ value, 7) && test_bit(emu->m6502->a ^ (result & 0x00ff), 7) ) emu->m6502->status |= FLAG_V;
    emu->m6502->a = ( BYTE )( result & 0x00ff );
    solve_flags_nz( emu->m6502->a );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_and( int mode ) {
    emu->m6502->a &= read_memory( solve_address(mode) );
    solve_flags_nz( emu->m6502->a );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_asl( int mode ) {
    WORD address = ( mode != ACC ? solve_address(mode) : 0 );
    BYTE operand = ( mode != ACC ? read_memory(address) : emu->m6502->a );
    emu->m6502->status &= ~( FLAG_N | FLAG_Z | FLAG_C );
    if( test_bit(operand, 7) ) emu->m6502->status |= FLAG_C;
    operand <<= 1;
    operand &= 0xfe;
    solve_flags_nz( operand );
    if( mode != ACC ) write_memory( address, operand ); else emu->m6502->a = operand;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_bcc() {
    emu->m6502->pc.w++;
    if( !test_flag(FLAG_C) ) emu->m6502->pc.w = solve_address( REL );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_bcs() {
    emu->m6502->pc.w++;
    if( test_flag(FLAG_C) ) emu->m6502->pc.w = solve_address( REL );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_beq() {
    emu->m6502->pc.w++;
    if( test_flag(FLAG_Z) ) emu->m6502->pc.w = solve_address( REL );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_bit( int mode ) {
    BYTE value = read_memory( solve_address(mode) );
    emu->m6502->status &= ~( FLAG_N | FLAG_Z | FLAG_V );
    if( (emu->m6502->a & value) == 0 ) emu->m6502->status |= FLAG_Z;
    if( test_bit(value, 6) ) emu->m6502->status |= FLAG_V;
    if( test_bit(value, 7) ) emu->m6502->status |= FLAG_N;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_bmi() {
    emu->m6502->pc.w++;
    if( test_flag(FLAG_N) ) emu->m6502->pc.w = solve_address( REL );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_bne() {
    emu->m6502->pc.w++;
    if( !test_flag(FLAG_Z) ) emu->m6502->pc.w = solve_address( REL );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_bpl() {
    emu->m6502->pc.w++;
    if( !test_flag(FLAG_N) ) emu->m6502->pc.w = solve_address( REL );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_bvc() {
    emu->m6502->pc.w++;
    if( !test_flag(FLAG_V) ) emu->m6502->pc.w = solve_address( REL );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_bvs() {
    emu->m6502->pc.w++;
    if( test_flag(FLAG_V) ) emu->m6502->pc.w = solve_address( REL );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_clc() {
    emu->m6502->status &= ~FLAG_C;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_cld() {
    emu->m6502->status &= ~FLAG_D;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_clv() {
    emu->m6502->status &= ~FLAG_V;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_cmp( int mode ) {
    BYTE value = read_memory( solve_address(mode) );
    BYTE result = emu->m6502->a - value;
    solve_flags_nz( result );
    emu->m6502->status &= ~FLAG_C;
    if( emu->m6502->a >= value ) emu->m6502->status |= FLAG_C;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_cpx( int mode ) {
    BYTE value = read_memory( solve_address(mode) );
    BYTE result = emu->m6502->x - value;
    solve_flags_nz( result );
    emu->m6502->status &= ~FLAG_C;
    if( emu->m6502->x >= value ) emu->m6502->status |= FLAG_C;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_cpy( int mode ) {
    BYTE value = read_memory( solve_address(mode) );
    BYTE result = emu->m6502->y - value;
    solve_flags_nz( result );
    emu->m6502->status &= ~FLAG_C;
    if( emu->m6502->y >= value ) emu->m6502->status |= FLAG_C;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_dec( int mode ) {
    WORD address = solve_address( mode );
    BYTE value = read_memory( address ) - 1;
    solve_flags_nz( value );
    write_memory( address, value );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_dex() {
    emu->m6502->x--;
    solve_flags_nz( emu->m6502->x );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_dey() {
    emu->m6502->y--;
    solve_flags_nz( emu->m6502->y );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_eor( int mode ) {
    emu->m6502->a ^= read_memory( solve_address(mode) );
    solve_flags_nz( emu->m6502->a );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_inc( int mode ) {
    WORD address = solve_address( mode );
    BYTE value = read_memory( address ) + 1;
    solve_flags_nz( value );
    write_memory( address, value );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_inx() {
    emu->m6502->x++;
    solve_flags_nz( emu->m6502->x );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_iny() {
    emu->m6502->y++;
    solve_flags_nz( emu->m6502->y );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_jmp( int mode ) {
    emu->m6502->pc.w = solve_address( mode );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_jsr() {
    PAIR_REGISTER temp = emu->m6502->pc;
    temp.w++;
    stack_push( temp.h );
    stack_push( temp.l );
    emu->m6502->pc.w = solve_address( ABS );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_lda( int mode ) {
    emu->m6502->a = read_memory( solve_address(mode) );
    solve_flags_nz( emu->m6502->a );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_ldx( int mode ) {
    emu->m6502->x = read_memory( solve_address(mode) );
    solve_flags_nz( emu->m6502->x );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_ldy( int mode ) {
    emu->m6502->y = read_memory( solve_address(mode) );
    solve_flags_nz( emu->m6502->y );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_lsr( int mode ) {
    WORD address = ( mode != ACC ? solve_address(mode) : 0 );
    BYTE operand = ( mode != ACC ? read_memory(address) : emu->m6502->a );
    emu->m6502->status &= ~( FLAG_N | FLAG_Z | FLAG_C );
    if( test_bit(operand, 0) ) emu->m6502->status |= FLAG_C;
    operand >>= 1;
    operand &= 0x7f;
    if( !operand) emu->m6502->status |= FLAG_Z;
    if( mode != ACC ) write_memory( address, operand ); else emu->m6502->a = operand;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_nop() { /* empty... */ }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_ora( int mode ) {
    emu->m6502->a |= read_memory( solve_address(mode) );
    solve_flags_nz( emu->m6502->a );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_pha() {
    stack_push( emu->m6502->a );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_php() {
    stack_push( (BYTE)(emu->m6502->status | 0x30) );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_pla() {
    emu->m6502->a = stack_pop();
    solve_flags_nz( emu->m6502->a );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_plp() {
    emu->m6502->status = stack_pop();
    emu->m6502->status &= ~FLAG_B;
    emu->m6502->status |= FLAG_P;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_ror( int mode ) {
    WORD address = ( mode != ACC ? solve_address(mode) : 0 );
    BYTE operand = ( mode != ACC ? read_memory(address) : emu->m6502->a );
    int old_carry = test_flag( FLAG_C );
    emu->m6502->status &= ~FLAG_C;
    if( test_bit(operand, 0) ) emu->m6502->status |= FLAG_C;
    operand >>= 1;
    operand &= 0x7f;
    operand |= ( old_carry << 7 );
    solve_flags_nz( operand );
    if( mode != ACC ) write_memory( address, operand ); else emu->m6502->a = operand;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_rol( int mode ) {
    WORD address = ( mode != ACC ? solve_address(mode) : 0 );
    BYTE operand = ( mode != ACC ? read_memory(address) : emu->m6502->a );
    int old_carry = test_flag( FLAG_C );
    emu->m6502->status &= ~FLAG_C;
    if( test_bit(operand, 7) ) emu->m6502->status |= FLAG_C;
    operand <<= 1;
    operand &= 0xfe;
    operand |= old_carry;
    solve_flags_nz( operand );
    if( mode != ACC ) write_memory( address, operand ); else emu->m6502->a = operand;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_rti() {
    emu->m6502->status = stack_pop();
    emu->m6502->pc.l = stack_pop();
    emu->m6502->pc.h = stack_pop();
    emu->m6502->status &= ~FLAG_B;
    emu->m6502->status |= FLAG_P;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_rts() {
    //dump_memory( emu->ram, 0x10000 );
    //log_trace( 0x60 );
    emu->m6502->pc.l = stack_pop();// printf(" L:%02X ", emu->m6502->pc.l);
    emu->m6502->pc.h = stack_pop();// printf(" H:%02X ", emu->m6502->pc.h);
    emu->m6502->pc.w++;
    /*printf("\nPC:%04X OP: %02X @ %02X%02X\n",
           emu->m6502->pc.w,
           read_memory(emu->m6502->pc.w),
           read_memory(emu->m6502->pc.w+2),
           read_memory(emu->m6502->pc.w+1) );*/
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_sbc( int mode ) {
    BYTE value = read_memory( solve_address(mode) );
    WORD result = ( WORD )( emu->m6502->a - value - (!test_flag(FLAG_C)) );
    emu->m6502->status &= ~( FLAG_C | FLAG_V );
    if( result < 0x0100 ) emu->m6502->status |= FLAG_C;
    if( test_bit(emu->m6502->a ^ (result & 0x00ff), 7) && test_bit(emu->m6502->a ^ value, 7) ) emu->m6502->status |= FLAG_V;
    emu->m6502->a = ( BYTE )( result & 0x00ff );
    solve_flags_nz( emu->m6502->a );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_sec() {
    emu->m6502->status |= FLAG_C;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_sed() {
    emu->m6502->status |= FLAG_D;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_sei() {
    emu->m6502->status |= FLAG_I;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_sta( int mode ) {
    write_memory( solve_address(mode), emu->m6502->a );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_stx( int mode ) {
    write_memory( solve_address(mode), emu->m6502->x );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_sty( int mode ) {
    write_memory( solve_address(mode), emu->m6502->y );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_tax() {
    emu->m6502->x = emu->m6502->a;
    solve_flags_nz( emu->m6502->x );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_tay() {
    emu->m6502->y = emu->m6502->a;
    solve_flags_nz( emu->m6502->y );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_tsx() {
    emu->m6502->x = emu->m6502->sp;
    solve_flags_nz( emu->m6502->x );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_txa() {
    emu->m6502->a = emu->m6502->x;
    solve_flags_nz( emu->m6502->a );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_txs() {
    emu->m6502->sp = emu->m6502->x; 
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void opcode_tya() {
    emu->m6502->a = emu->m6502->y;
    solve_flags_nz( emu->m6502->a );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////